<script lang="ts" setup>
import { onMounted, reactive, ref } from 'vue'
import { size } from '@/variables.ts'
import { invoke_proxy } from '@/functions/invoke.ts'
import { Command } from '@/commands/command.ts'
import { useMessage } from 'naive-ui'
import CommandComponent from '@/views/commands/CommandComponent.vue'
import { command_suggestions, new_command } from '@/commands/commands.ts'
import { show_component_color_window, show_component_v2ray_window, show_help_window, show_setting_window } from '@/functions/window.ts'
import { use_global_state, use_ui_setting } from '@/state/pinia.ts'
import { dbus } from '@/dbus/dbus.ts'
import { BackendResponse } from '@/runtime/response.ts'
import { Command as ModelCommand } from '@/models/command.ts'
import { BACKEND_CODE_CONFIG_NOT_EXISTS, BACKEND_CODE_OK } from '@/runtime/code.ts'
import { ArrowMaximize32Filled } from '@vicons/fluent'
import { BasicCommandSetting, new_basic_command_setting } from '@/models/commands/basic_command_setting.ts'

const commands = ref(null)
const input = ref(null)
const input_extend = ref(null)
const global_state = use_global_state()
const ui_setting = use_ui_setting()
const state = reactive<{ input: string; input_extend: string; focus: boolean; commands: Command[]; suggestions: string[]; suggestion_index: number; histories: string[]; history_index: number; extend: boolean }>({ input: '', focus: false, commands: [], suggestions: [], suggestion_index: 0, histories: [], history_index: -1, extend: false, input_extend: '' })

onMounted(async () => {
    window.Message = useMessage()

    dbus.on('scroll-to-bottom', () => {
        const element = commands.value! as HTMLElement
        element.scrollTop = element.scrollHeight
    })

    document.addEventListener('click', async function (event) {
        const target = event.target as HTMLElement
        if (target.tagName.toLowerCase() === 'a') {
            event.preventDefault()

            const link = target as HTMLLinkElement
            const response = await invoke_proxy<BackendResponse<any>>('invoke_open', { path: link.href })
            if (BACKEND_CODE_OK !== response.code) {
                window.Message.error(response.message)

                return
            }
        }
    })

    // 监听按键按下事件
    document.onkeydown = async (event: KeyboardEvent) => {
        // 禁用tab索引
        if (event.key === 'Tab') {
            event.preventDefault()
        }

        if ('w' === event.key.toLowerCase() && event.metaKey) {
            event.preventDefault()

            if (global_state.allow_close_window) {
                close_window().finally()

                return
            }
        }
    }

    // 监听键盘释放事件
    document.onkeyup = async (event: KeyboardEvent) => {
        if (event.key === '/') {
            if (state.extend) {
                const element = input_extend.value! as HTMLElement
                element.focus()
            } else {
                const element = input.value! as HTMLElement
                element.focus()
            }

            return
        }

        if (event.key === ',' && event.shiftKey && event.metaKey) {
            await show_setting_window()

            return
        }

        if (event.key.toLowerCase() === 'h' && event.shiftKey && event.metaKey) {
            await show_help_window()

            return
        }

        if (event.key.toLowerCase() === 'b' && event.shiftKey && event.metaKey) {
            await show_component_v2ray_window()

            return
        }

        if (event.key.toLowerCase() === 'r' && event.metaKey) {
            window.location.reload()

            return
        }
    }

    // 加载命令历史
    const response = await invoke_proxy<BackendResponse<ModelCommand[]>>('invoke_get_command_history')
    if (BACKEND_CODE_OK !== response.code) {
        window.Message.error(response.message)

        return
    }

    for (const command of response.data.reverse()) {
        const c = new Command(command, true)
        state.commands.push(c)
        state.histories.unshift(c.toString())
    }

    const input_element = input.value! as HTMLElement
    input_element.focus()

    setTimeout(async () => dbus.emit('scroll-to-bottom'), 500)
})

function execute_specified_command(command: string): boolean {
    switch (command) {
        case 'help':
            show_help_window().finally()

            return true
        case 'setting':
            show_setting_window().finally()

            return true
        case 'v2ray':
            show_component_v2ray_window().finally()

            return true
        case 'color':
            show_component_color_window().finally()

            return true
    }
    return false
}

const executes: Record<string, (event: KeyboardEvent, input: string) => Promise<void>> = {
    Enter: execute_enter,
    ArrowDown: execute_arrow,
    ArrowUp: execute_arrow,
    Tab: execute_tab,
    Backspace: execute_backspace,
}

async function execute(event: KeyboardEvent): Promise<void> {
    let input = state.extend ? state.input_extend.trim() : state.input.trim()

    if (executes.hasOwnProperty(event.key)) {
        await executes[event.key](event, input)
    }

    state.history_index = -1
    state.suggestion_index = 0
    // 根据输入显示建议命令
    state.suggestions = command_suggestions(input)
}

async function execute_enter(event: KeyboardEvent, input: string): Promise<void> {
    // Shift + Enter切换为扩展模式
    if (event.shiftKey && !state.extend) {
        await switch_extend(true)
        state.input_extend += '\n'
        const html_element = input_extend.value! as HTMLElement
        html_element.focus()

        return
    }

    const normal_enter = !event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey
    const ctrl_enter = event.ctrlKey && !event.metaKey && !event.shiftKey && !event.altKey
    const meta_enter = event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey

    ui_setting.initialize()
    if (!('Enter' === ui_setting.send_mode && normal_enter) && !('Ctrl+Enter' === ui_setting.send_mode && ctrl_enter) && !('Meta+Enter' === ui_setting.send_mode && meta_enter)) {
        if (!state.extend && normal_enter) {
            await switch_extend(true)
            state.input_extend += '\n'

            const html_element = input_extend.value! as HTMLElement
            html_element.focus()
        }

        return
    }

    state.input = state.input_extend = ''
    state.extend = false

    // 重置统计信息
    state.suggestions = []
    state.suggestion_index = 0
    state.history_index = -1

    // 加入历史命令列表
    state.histories.unshift(input)

    // 特殊命令判断
    if (execute_specified_command(input)) {
        return
    }

    if (input.length > 0) {
        let default_args = ''
        if ('search' === ui_setting.default_command) {
            default_args = { Google: '', Baidu: '--baidu', Bing: '--bing' }[ui_setting.default_search_engine] as string
        }

        const command = new_command(input, ui_setting.default_command, default_args)
        if (null === command) {
            return
        }

        let basic_command_setting = { basic_command_setting: new_basic_command_setting() }
        {
            // 加载配置
            const response = await invoke_proxy<BackendResponse<string>>('invoke_get_setting', { name: `command.${command.name}.config` })
            if (BACKEND_CODE_OK !== response.code && BACKEND_CODE_CONFIG_NOT_EXISTS !== response.code) {
                window.Message.error(response.message)

                return
            }

            if (response.data) {
                basic_command_setting = JSON.parse(response.data) as { basic_command_setting: BasicCommandSetting }
            }
        }

        if (basic_command_setting.basic_command_setting.append_to_history) {
            state.commands.push(command)
            if (state.commands.length > 30) {
                state.commands.shift()
            }
        } else {
            command.execute().finally()
        }

        if (basic_command_setting.basic_command_setting.autoclose_window_after_execute) {
            close_window().finally()
        }
    }
}

async function execute_arrow(event: KeyboardEvent, _: string): Promise<void> {
    switch (event.key) {
        case 'ArrowDown':
            if (0 !== state.histories.length) {
                state.history_index--
                if (state.history_index < -1) {
                    state.history_index = -1
                }

                state.input = ''
                if (0 <= state.history_index) {
                    state.input = state.histories[state.history_index]
                }
            }

            break
        case 'ArrowUp':
            if (0 !== state.histories.length) {
                state.history_index++
                if (state.history_index > state.histories.length - 1) {
                    state.history_index = state.histories.length - 1
                }

                state.input = state.histories[state.history_index]
            }

            break
    }
}

async function execute_tab(_: KeyboardEvent, input: string): Promise<void> {
    // 根据建议自动补全
    const tokens = input.split(' ')
    if (1 === tokens.length && 0 !== state.suggestions.length) {
        if (state.suggestion_index >= state.suggestions.length) {
            state.suggestion_index = 0
        }

        state.input = state.suggestions[state.suggestion_index++].split(' ')[0]
    }
}

async function execute_backspace(_: KeyboardEvent, _input: string): Promise<void> {
    if (state.extend && !_input.includes('\n')) {
        await switch_extend(false)

        const input_element = input.value! as HTMLElement
        input_element.focus()
    }
}

async function close_window(): Promise<void> {
    await invoke_proxy('invoke_close_window').finally()
}

async function switch_extend(extend: boolean): Promise<void> {
    if (extend) {
        state.input_extend = state.input.trim()
        state.extend = true
    } else {
        state.input = state.input_extend.trim()
        state.extend = false
    }
}

async function action_send(): Promise<void> {
    await execute(new KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: 'Enter', ctrlKey: 'Ctrl+Enter' === ui_setting.send_mode, metaKey: 'Meta+Enter' === ui_setting.send_mode }))
}
</script>

<template>
    <n-space :size="15" :wrap-item="false" class="endpoint" vertical>
        <div ref="commands" class="commands max-flex-width">
            <n-space :size="15" :wrap-item="false" vertical>
                <template v-for="command in state.commands" v-if="0 !== state.commands.length" :key="command.id">
                    <n-space :size="10" :wrap-item="false" class="command-line" vertical>
                        <CommandComponent :command="command as Command"></CommandComponent>
                        <component :is="command.component" :command="command"></component>
                    </n-space>
                </template>
                <template v-if="0 === state.commands.length">
                    <n-space :size="35" :wrap-item="false" class="helps" vertical>
                        <n-space :wrap="false" :wrap-item="false" align="center" class="help-info">
                            <div>输入help命令打开帮助手册</div>
                        </n-space>
                        <n-space :wrap="false" :wrap-item="false" align="center" class="help-info">
                            <div>按下Esc键关闭当前窗口</div>
                        </n-space>
                    </n-space>
                </template>
            </n-space>
        </div>
        <n-space :size="15" :wrap="false" :wrap-item="false" class="auto-complete" vertical>
            <template v-for="suggestion in state.suggestions" :key="suggestion">
                <div class="suggestion">{{ suggestion }}</div>
            </template>
        </n-space>
        <n-space :wrap="false" :wrap-item="false" align="center" class="max-width command-input" justify="space-between">
            <div class="title" data-tauri-drag-region>Magic Box</div>
            <div class="max-flex-width">
                <template v-if="state.extend">
                    <n-input ref="input_extend" v-model:value="state.input_extend" :placeholder="state.focus ? (0 === state.commands.length ? '输入命令或问题，支持Tab自动补全' : '输入命令或问题，支持Tab自动补全，输入help命令打开帮助手册') : '输入命令或问题，按/获取焦点'" :resizable="false" :size="size" autocomplete="off" class="max-width terminal" rows="3" spellcheck="false" tabindex="-1" type="textarea" @blur="state.focus = false" @focus="state.focus = true" @keyup="execute">
                        <template v-slot:suffix>
                            <n-icon class="pointer" @click="switch_extend(false)">
                                <ArrowMaximize32Filled></ArrowMaximize32Filled>
                            </n-icon>
                        </template>
                    </n-input>
                </template>
                <template v-if="!state.extend">
                    <n-input ref="input" v-model:value="state.input" :placeholder="state.focus ? (0 === state.commands.length ? '输入命令或问题，支持Tab自动补全' : '输入命令或问题，支持Tab自动补全，输入help命令打开帮助手册') : '输入命令或问题，按/获取焦点'" :size="size" autocomplete="off" class="max-width terminal" spellcheck="false" tabindex="-1" @blur="state.focus = false" @focus="state.focus = true" @keyup="execute">
                        <template v-slot:suffix>
                            <n-icon class="pointer" @click="switch_extend(true)">
                                <ArrowMaximize32Filled></ArrowMaximize32Filled>
                            </n-icon>
                        </template>
                    </n-input>
                </template>
            </div>
            <div>
                <n-button size="small" type="info" @click="action_send">发送</n-button>
            </div>
        </n-space>
    </n-space>
</template>

<style lang="scss" scoped>
@import '@/assets/scss/variables';

.endpoint {
    height: calc(100vh - 30px);
    padding: 15px;
}

.divider {
    height: 1px;
    width: 100%;
    background-color: $color-divider;
}

.title {
    font-size: 18px;
    font-weight: 600;
    user-select: none;
    -webkit-user-select: none;
    -ms-user-select: none;
    cursor: default;
}

.terminal {
    font-family: monospace;
}

.commands {
    overscroll-behavior: none;
    overflow-y: auto;
}

.command-line {
    margin-top: 35px;
    margin-bottom: 15px;
}

.helps {
    margin-top: 35vh;
}

.help-info {
    margin: 0 auto;
    color: $color-gray;
}

.auto-complete {
    background-color: $color-suggestion;
    width: calc(100vw - 60px);
    min-height: 60px;
    max-height: 60px;
    overflow-y: auto;
    padding: 15px;
}

.extend-input {
    min-height: 60px;
    max-height: 120px;
}
</style>
